package com.binance.gateway.filter.time;

import com.binance.gateway.app.base.helper.BaseHelper;
import com.binance.gateway.app.constant.Constant;
import com.binance.gateway.constant.GwFilterOrders;
import com.binance.master.utils.WebUtils;
import com.ctrip.framework.apollo.ConfigService;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Enumeration;

/**
 * 记录请求开始时间
 */
@Component
@Slf4j
public class LogStartTimeFilter extends ZuulFilter {
    private String[] DEFAULT_DISPLAY_HEADER = new String[]{"x-forwarded-for", "http_x_forwarded_for", "user-agent", "referer", "versionname", "clienttype", "versioncode"};

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        // header中不允许外部传入X-USER-ID,
        clearHeader(ctx, request, Constant.HEADER_USER_ID);
        // header中不允许外部传入X-USER-EMAIL
        clearHeader(ctx, request, Constant.HEADER_USER_EMAIL);
        
        long startTime = System.currentTimeMillis();
        ctx.set("startTime", startTime);
        boolean isShowAllHeader = StringUtils.equalsAnyIgnoreCase(System.getProperty("env"), "dev", "qa");
        String header = getHeader(request, isShowAllHeader);
        String awsTrace = request.getHeader(Constant.X_AMZN_TRACE_ID);
        if (StringUtils.isNotBlank(awsTrace)) {
            try {
                awsTrace = awsTrace.substring(awsTrace.indexOf("Root=") + 5);
            } catch (Exception e) { // Do nothing
            }
        }
        String param = "skip";
        if (isShowAllHeader || StringUtils.equals(ConfigService.getAppConfig().getProperty("log.request", "N"), "Y")) {
            param = getParam(request);
        }
        String tokenSegment = BaseHelper.getTokenSegment(request);
        log.info("method={}, requestUrl={}, header={}, X-UI-REQUEST-TRACE={}, x-amzn-trace-id={} , param={},userIP={},userId={},segment={}",
                request.getMethod(), request.getRequestURL().toString(), header,
                request.getHeader(Constant.HEADER_UI_REQUEST_TRACE), awsTrace, param, WebUtils.getRequestIp(), BaseHelper.transformUserIdFromToken(tokenSegment), tokenSegment);
        return null;
    }

    private void clearHeader(RequestContext ctx, HttpServletRequest request, String headerName) {
        if (StringUtils.isNotEmpty(request.getHeader(headerName))) {
            ctx.addZuulRequestHeader(headerName, "");
        }
    }


    /**
     * 获取header
     * 
     * @param request
     * @param isShowAllHeader
     * @return
     */
    private String getHeader(HttpServletRequest request, boolean isShowAllHeader) {
        StringBuilder builder = new StringBuilder();
        Enumeration<String> names = request.getHeaderNames();
        if (isShowAllHeader) {
            while (names.hasMoreElements()) {
                String name = names.nextElement();
                builder.append(name).append(":").append(request.getHeader(name)).append(";");
            }
        } else {
            // 生产仅打印指定header，防止日志过大
            String[] displayHeaders = ConfigService.getAppConfig().getArrayProperty("display.header", ",", DEFAULT_DISPLAY_HEADER);
            for (String name : displayHeaders) {
                if (!isSensitiveHeader(name)) {
                    // 防止打印敏感信息
                    builder.append(name).append(":").append(request.getHeader(name)).append(";");
                }
            }
        }
        return builder.toString();
    }


    /**
     * 获取参数
     * 
     * @param request
     * @return
     */
    private String getParam(HttpServletRequest request) {
        if (HttpMethod.GET.toString().equals(request.getMethod())) {
            StringBuilder builder = new StringBuilder();
            Enumeration<String> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = paramNames.nextElement();
                builder.append(paramName).append(":").append(request.getParameter(paramName)).append(",");
            }
            return builder.toString();
        } else {
            final RequestContext ctx = RequestContext.getCurrentContext();
            if (!ctx.isChunkedRequestBody()) {
                ServletInputStream inputStream = null;
                try {
                    inputStream = ctx.getRequest().getInputStream();
                    String body = null;
                    if (inputStream != null) {
                        body = IOUtils.toString(inputStream, Charset.forName("UTF-8"));
                        return body;
                    }
                } catch (IOException e) {
                    log.warn("getParam error", e);
                }
            } else {
                log.info("isChunkedRequestBody = false");
            }
            return "";
        }
    }


    private boolean isSensitiveHeader(String name) {
        return StringUtils.equalsIgnoreCase(name, Constant.HEADER_TOKEN) || StringUtils.equalsIgnoreCase(name, "cookie") || StringUtils.equalsIgnoreCase(name, "token")
                || StringUtils.equalsIgnoreCase(name, Constant.COOKIE_CSRFTOKEN)
                || StringUtils.equalsIgnoreCase(name, Constant.COOKIE_NEW_CSRFTOKEN);
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public int filterOrder() {
        return GwFilterOrders.LOG_START;
    }

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
}
